"use client"; import { useEffect, useState } from "react"; import { useParams, useRouter } from "next/navigation"; import useSWR, { mutate as gmutate } from "swr"; import { FolderKanban, Archive, Trash2, Check, AlertTriangle } from "lucide-react"; import { api, fetcher } from "@/lib/client/fetcher"; import type { ArtifactDTO, MemoryDTO, ProjectDTO } from "@/lib/client/types"; import { Button, Spinner, Tabs, cn, toast } from "@/components/ui"; import { ColorPicker } from "@/components/projects/project-shared"; import { ArtifactsTab, ConversationsTab, FilesTab, InstructionsTab, MemoryTab, NotesTab, type ProjectFile } from "@/components/projects/project-tabs"; interface Resp { project: ProjectDTO; files: ProjectFile[]; memories: MemoryDTO[]; artifacts: ArtifactDTO[] } type Tab = "conversations" | "instructions" | "files" | "memory" | "notes" | "artifacts"; export default function ProjectPage() { const { id } = useParams<{ id: string }>(); const router = useRouter(); const { data, error, mutate } = useSWR(`/api/projects/${id}`, fetcher); const [tab, setTab] = useState("conversations"); const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [editingHead, setEditingHead] = useState(false); useEffect(() => { if (data) { setName(data.project.name); setDescription(data.project.description ?? ""); } }, [data]); const refresh = () => { void mutate(); void gmutate("/api/projects"); }; async function patch(p: Record, msg?: string) { try { await api(`/api/projects/${id}`, { method: "PATCH", json: p }); if (msg) toast(msg, "ok"); refresh(); } catch (e) { toast((e as Error).message, "error"); } } async function saveHead() { await patch({ name: name.trim() || data?.project.name, description: description.trim() || null }, "Projet mis à jour"); setEditingHead(false); } async function archive() { if (!confirm("Archiver ce projet ? Ses conversations restent accessibles dans l'historique.")) return; await patch({ archived: true }, "Projet archivé"); router.push("/"); } async function del() { if (!confirm("Supprimer définitivement ce projet ? Les conversations sont conservées mais détachées ; les fichiers et souvenirs du projet sont supprimés.")) return; await api(`/api/projects/${id}`, { method: "DELETE" }); void gmutate("/api/projects"); toast("Projet supprimé", "ok"); router.push("/"); } if (error) return
Projet introuvable.
; if (!data) return
; const p = data.project; const tabs: { value: Tab; label: string; count?: number }[] = [ { value: "conversations", label: "Conversations", count: p.conversation_count }, { value: "instructions", label: "Instructions" }, { value: "files", label: "Fichiers", count: data.files.length }, { value: "memory", label: "Mémoire", count: data.memories.length }, { value: "notes", label: "Notes" }, { value: "artifacts", label: "Artefacts", count: data.artifacts.length }, ]; return (
{editingHead ? (
setName(e.target.value)} maxLength={120} className="input text-lg font-semibold" autoFocus /> setDescription(e.target.value)} maxLength={2000} className="input text-[13px]" placeholder="Description" />
patch({ color: c })} />
) : ( )}
({ value: t.value, label: {t.label}{t.count != null ? {t.count} : null} }))} />
{tab === "conversations" && } {tab === "instructions" && } {tab === "files" && } {tab === "memory" && } {tab === "notes" && } {tab === "artifacts" && }

Zone sensible

Archiver masque le projet de la barre latérale sans rien supprimer. Supprimer efface les fichiers, notes et souvenirs du projet ; les conversations sont conservées sans projet.

); }